package com.kpmg.datalake.operatelog.service;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.kpmg.datalake.operatelog.annotation.OptLog;
import com.kpmg.datalake.operatelog.model.OperateLog;
import eu.bitwalker.useragentutils.UserAgent;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

/**
 * @author Alex.zhao [alex.zhao@kpmg.com]
 * @since Created on azhao6 on 2018-08-08 15:36:38
 */
@Service
public class HandleLog implements ApplicationContextAware {

    private static ApplicationContext context = null;

    @Async
    public void doHandlerLog(ProceedingJoinPoint pjp, Throwable stackLog, Object result, UserAgent userAgent, OperateLog operateLog,
        RequestAttributes requestAttributes) {
      RequestContextHolder.setRequestAttributes(requestAttributes);
        //获取bean对象
        IOperateLogService operateLogService = context.getBean(IOperateLogService.class);
        final MethodSignature signature = (MethodSignature) pjp.getSignature();
        OptLog optLog = signature.getMethod().getAnnotation(OptLog.class);
        operateLog.setLogDesc(optLog.description());
        operateLog.setLogType(optLog.logType().getCode());
        operateLog.setLogModule(optLog.module());
        operateLog.setClassName(pjp.getTarget().getClass().getName());
        operateLog.setMethodName(signature.getName());
        //处理入参
        handParam(pjp, userAgent, signature, operateLog);
        //处理返回结果
        handResult(stackLog, result, operateLog);
        Long endTime = System.currentTimeMillis();
        operateLog.setEndTime(new Date(endTime));
        operateLog.setCostTime(endTime - operateLog.getBeginTime().getTime());
        operateLogService.insert(operateLog);
    }

    /**
     * 处理返回结果
     *
     * @param stackLog   日志信息
     * @param result     结果
     * @param operateLog operateLog
     */
    private void handResult(Throwable stackLog, Object result, OperateLog operateLog) {
        if (stackLog != null) {
            StringBuilder sb = new StringBuilder();
            for (StackTraceElement stackTraceElement : stackLog.getStackTrace()) {
                sb.append(stackTraceElement);
                sb.append(" \r\n ");
            }
            Map<String, String> stack = new HashMap<>(12);
            stack.put("message", stackLog.getMessage());
            stack.put("stackTrace", sb.toString());
            operateLog.setRequestResult(JSONObject.toJSONString(stack, SerializerFeature.DisableCircularReferenceDetect));
        } else if (result != null) {
            operateLog.setRequestResult(JSONObject.toJSONString(result, SerializerFeature.DisableCircularReferenceDetect));
        }
    }

    /**
     * 处理入参
     *
     * @param pjp        pjp
     * @param userAgent  埋点信息
     * @param signature  signature
     * @param operateLog operateLog
     */
    private void handParam(ProceedingJoinPoint pjp, UserAgent userAgent, MethodSignature signature, OperateLog operateLog) {
        String[] parameterNames = signature.getParameterNames();
        Object[] args = pjp.getArgs();
        if (parameterNames != null && parameterNames.length > 0) {
            JSONObject jsonObject = new JSONObject();
            for (int i = 0; i < parameterNames.length; i++) {
                String name = parameterNames[i];
                Object value = args[i];
                if (value != null) {
                    try {
                        JSONObject.toJSONString(value, SerializerFeature.DisableCircularReferenceDetect);
                        jsonObject.put(name, value);
                    } catch (Exception e) {
                        jsonObject.put(name, value.toString());
                    }
                } else {
                    jsonObject.put(name, "");
                }
            }
            jsonObject.put("userAgent", userAgent);
            operateLog.setRequestParam(jsonObject.toJSONString());
        }
    }

    @Override
    public synchronized void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }
}
